import math

from rpy2.rlike.container import OrdDict
from rpy2.robjects import default_converter, pandas2ri
import rpy2.robjects as robjects
from rpy2.robjects.conversion import localconverter
import pandas as pd
from utils.analysis.common.mediation_common_class import RegressionDetails, RegressionModel, CFAResult, CFAResultFi, \
    CFAResultFactorDetails, CFAResultFactor

# pandas2ri.activate()

# 定义R语言脚本-CFA算法
r_script = """
    func_cfa <- function(data, model) {
        library(lavaan)
        #library(semPlot)

        # 拟合SEM, estimator：最小二乘（"ML"）、最小二乘法（"WLS"）和广义最小二乘法（"GLS"）。
        fit <- sem(model, data=data,estimator="ML")

        # 提取结果
        summary_res <- summary(fit, standardized=TRUE)

        # 验证结果的准确性
        # res = fitMeasures(fit)
        fi_res <- fitMeasures(fit, c("chisq","df","nfi","rfi","ifi","tli","cfi","gfi","rmsea"))

        result_list <- list(summary_res = summary_res, fi_res = fi_res)
        result_list
    }
    """

r_script_v2 = """
    func_cfa <- function(data, model) {
        library(lavaan)
        #library(semPlot)

        # 拟合SEM, estimator：最小二乘（"ML"）、最小二乘法（"WLS"）和广义最小二乘法（"GLS"）。
        fit <- lavaan::cfa(model, data=data,estimator="ML", std.lv=FALSE, auto.cov.lv.x=FALSE, auto.cov.y=FALSE)

        # 提取结果
        #summary_res <- summary(fit, standardized=TRUE)
        summary_res <- summary(fit, standardized=TRUE, fit.measures=T)

        # 验证结果的准确性
        # res = fitMeasures(fit)
        fi_res <- fitMeasures(fit, c("chisq","df","nfi","rfi","ifi","tli","cfi","gfi","rmsea"))

        result_list <- list(summary_res = summary_res, fi_res = fi_res)
        result_list
    }
    """


def define_model(var_dict: dict, relations: []):
    # 动态构建模型字符串
    # cur_str = "# 测量模型\n"
    cur_str = ""
    for k, v in var_dict.items():
        cur_str += f"{k} =~ {' + '.join(v)}\n"
    for i in relations:
        cur_str += f"{i.from_name} ~~ {i.to_name}\n"
    return cur_str


class CFA:
    def __init__(self, df, var_dict, relations):
        self.df = df
        self.var_dict = var_dict
        self.relations = relations

    def analysis(self) -> CFAResult:
        with localconverter(default_converter + pandas2ri.converter):
            # 将Python的DataFrame转换为R的DataFrame
            r_df = pandas2ri.py2rpy(self.df)
            model = define_model(self.var_dict, self.relations)
            model_dict = self.var_dict
            # 将R语言脚本加载到R环境中
            robjects.r(r_script)
            result = robjects.r['func_cfa'](r_df, model)
            fi_res = result.get('fi_res')
            # 计算表1数据
            if len(fi_res) == 9:
                cmin = fi_res[0]
                df_value = fi_res[1]
                cmin_df = fi_res[0] / fi_res[1]
                nfi = fi_res[2]
                rfi = fi_res[3]
                ifi = fi_res[4]
                tli = fi_res[5]
                cfi = fi_res[6]
                gfi = fi_res[7]
                rmsea = fi_res[8]
                fi_obj = CFAResultFi(cmin=cmin, df_value=df_value, cmin_df=cmin_df, nfi=nfi, rfi=rfi, ifi=ifi, tli=tli,
                                     cfi=cfi, gfi=gfi, rmsea=rmsea)
            summary_res = result.get('summary_res')
            pe_df = summary_res.get('pe')
            # 计算表2数据
            item_dict = dict()
            for k, v in model_dict.items():
                if v:
                    for j in v:
                        filtered_df = pe_df[(pe_df['lhs'] == k) & (pe_df['op'] == "=~") & (pe_df['rhs'] == j)]
                        if filtered_df.empty:
                            continue
                        cur_child = item_dict.get(k)
                        if cur_child:
                            details = cur_child.details
                            if details:
                                pass
                            else:
                                details = []
                            cur_row = filtered_df.iloc[0]
                            details.append(
                                CFAResultFactorDetails(name=j, est=cur_row['est'], se=cur_row['se'], z=cur_row['z'],
                                                       std_all=cur_row['std.all'], p=cur_row['pvalue']))
                            cur_child.details = details
                        else:
                            details = []
                            cur_row = filtered_df.iloc[0]
                            details.append(
                                CFAResultFactorDetails(name=j, est=cur_row['est'], se=cur_row['se'], z=cur_row['z'],
                                                       std_all=cur_row['std.all'], p=cur_row['pvalue']))
                            cur_child = CFAResultFactor(details=details)
                        item_dict[k] = cur_child

            # 计算AVE和CR
            # 1. 平方和，2. (1-平方)的和，3. 因子载荷的和
            for ki, vi in item_dict.items():
                cur_factor_sum = 0  # 因子和
                cur_factor_sq_sum = 0  # 因子平方和
                cur_1_x_sum = 0  # (1-平方)的和
                for detail in vi.details:
                    cur_f = detail.std_all
                    cur_f_sq = detail.std_all * detail.std_all
                    cur_1_x = 1 - cur_f_sq if cur_f_sq > 0 else 0
                    cur_factor_sum += cur_f
                    cur_factor_sq_sum += cur_f_sq
                    cur_1_x_sum += cur_1_x
                vi.ave = cur_factor_sq_sum / (cur_factor_sq_sum + cur_1_x_sum)
                vi.ave_k_sq = math.sqrt(vi.ave)
                vi.cr = cur_factor_sum / (cur_factor_sum + cur_1_x_sum)
            # 计算表3数据
            factor_dict = dict()
            factors = list(model_dict.keys())
            for i in range(len(factors)):
                for j in range(len(factors)):
                    if j > i:
                        filtered_df = pe_df[
                            (pe_df['lhs'] == factors[i]) & (pe_df['op'] == "~~") & (pe_df['rhs'] == factors[j])]
                        if filtered_df.empty:
                            continue
                        cur_child = factor_dict.get(factors[i])
                        if cur_child:
                            details = cur_child.details
                            if details:
                                pass
                            else:
                                details = []
                            cur_row = filtered_df.iloc[0]
                            details.append(
                                CFAResultFactorDetails(name=factors[j], est=cur_row['est'], se=cur_row['se'],
                                                       z=cur_row['z'], std_all=cur_row['std.all'], p=cur_row['pvalue']))
                            cur_child.details = details
                        else:
                            details = []
                            cur_row = filtered_df.iloc[0]
                            details.append(
                                CFAResultFactorDetails(name=factors[j], est=cur_row['est'], se=cur_row['se'],
                                                       z=cur_row['z'], std_all=cur_row['std.all'], p=cur_row['pvalue']))
                            cur_child = CFAResultFactor(details=details)
                        factor_dict[factors[i]] = cur_child
            # 3. 组装返回参数
            return CFAResult(fi_value=fi_obj, item_dict=item_dict, factor_dict=factor_dict)

    def analysis_v2(self) -> CFAResult:
        with localconverter(default_converter + pandas2ri.converter):
            # 将Python的DataFrame转换为R的DataFrame
            r_df = pandas2ri.py2rpy(self.df)
            model = define_model(self.var_dict, self.relations)
            model_dict = self.var_dict
            # 将R语言脚本加载到R环境中
            robjects.r(r_script_v2)
            result = robjects.r['func_cfa'](r_df, model)
            fi_res = result.get('fi_res')
            # 计算表1数据
            if len(fi_res) == 9:
                cmin = fi_res[0]
                df_value = fi_res[1]
                cmin_df = fi_res[0] / fi_res[1]
                nfi = fi_res[2]
                rfi = fi_res[3]
                ifi = fi_res[4]
                tli = fi_res[5]
                cfi = fi_res[6]
                gfi = fi_res[7]
                rmsea = fi_res[8]
                fi_obj = CFAResultFi(cmin=cmin, df_value=df_value, cmin_df=cmin_df, nfi=nfi, rfi=rfi, ifi=ifi, tli=tli,
                                     cfi=cfi, gfi=gfi, rmsea=rmsea)
            summary_res = result.get('summary_res')
            pe_df = summary_res.get('pe')
            # 计算表2数据
            item_dict = dict()
            for k, v in model_dict.items():
                if v:
                    for j in v:
                        filtered_df = pe_df[(pe_df['lhs'] == k) & (pe_df['op'] == "=~") & (pe_df['rhs'] == j)]
                        if filtered_df.empty:
                            continue
                        cur_child = item_dict.get(k)
                        if cur_child:
                            details = cur_child.details
                            if details:
                                pass
                            else:
                                details = []
                            cur_row = filtered_df.iloc[0]
                            details.append(
                                CFAResultFactorDetails(name=j, est=cur_row['est'], se=cur_row['se'], z=cur_row['z'],
                                                       std_all=cur_row['std.all'], p=cur_row['pvalue']))
                            cur_child.details = details
                        else:
                            details = []
                            cur_row = filtered_df.iloc[0]
                            details.append(
                                CFAResultFactorDetails(name=j, est=cur_row['est'], se=cur_row['se'], z=cur_row['z'],
                                                       std_all=cur_row['std.all'], p=cur_row['pvalue']))
                            cur_child = CFAResultFactor(details=details)
                        item_dict[k] = cur_child

            # 计算AVE和CR
            # 1. 平方和，2. (1-平方)的和，3. 因子载荷的和
            for ki, vi in item_dict.items():
                cur_factor_sum = 0  # 因子和
                cur_factor_sq_sum = 0  # 因子平方和
                cur_1_x_sum = 0  # (1-平方)的和
                for detail in vi.details:
                    cur_f = detail.std_all
                    cur_f_sq = detail.std_all * detail.std_all
                    cur_1_x = 1 - cur_f_sq if cur_f_sq > 0 else 0
                    cur_factor_sum += cur_f
                    cur_factor_sq_sum += cur_f_sq
                    cur_1_x_sum += cur_1_x
                vi.ave = cur_factor_sq_sum / (cur_factor_sq_sum + cur_1_x_sum)
                vi.ave_k_sq = math.sqrt(vi.ave)
                # 算法 x^2 / (x^2 + y)
                vi.cr = cur_factor_sum * cur_factor_sum / (cur_factor_sum * cur_factor_sum + cur_1_x_sum)
            # 计算表3数据
            factor_dict = dict()
            factors = list(model_dict.keys())
            for i in range(len(factors)):
                for j in range(len(factors)):
                    if j > i:
                        filtered_df = pe_df[
                            (pe_df['lhs'] == factors[i]) & (pe_df['op'] == "~~") & (pe_df['rhs'] == factors[j])]
                        if filtered_df.empty:
                            continue
                        cur_child = factor_dict.get(factors[i])
                        if cur_child:
                            details = cur_child.details
                            if details:
                                pass
                            else:
                                details = []
                            cur_row = filtered_df.iloc[0]
                            details.append(
                                CFAResultFactorDetails(name=factors[j], est=cur_row['est'], se=cur_row['se'],
                                                       z=cur_row['z'], std_all=cur_row['std.all'], p=cur_row['pvalue']))
                            cur_child.details = details
                        else:
                            details = []
                            cur_row = filtered_df.iloc[0]
                            details.append(
                                CFAResultFactorDetails(name=factors[j], est=cur_row['est'], se=cur_row['se'],
                                                       z=cur_row['z'], std_all=cur_row['std.all'], p=cur_row['pvalue']))
                            cur_child = CFAResultFactor(details=details)
                        factor_dict[factors[i]] = cur_child
            # 3. 组装返回参数
            return CFAResult(fi_value=fi_obj, item_dict=item_dict, factor_dict=factor_dict)


if __name__ == '__main__':
    pd.set_option('expand_frame_repr', False)
    df = pd.read_excel('./test_datas.xlsx')
    v_dict = dict()
    v_dict['F1'] = ["JG1", "JG2", "JG3", "JG4", "JG5"]
    v_dict['F2'] = ["XW1", "XW2", "XW3", "XW4"]
    v_dict['F3'] = ["JX1", "JX2", "JX3"]
    v_dict['F4'] = ["YY1", "YY2", "YY3"]
    obj = CFA(df, v_dict)
    result = obj.analysis()
    print(result)
